/**
 *@author  wuwg
 *@version      2018.0.1
 *@createTime   2018-07-18
 *@updateTime   2018-07-19
 *@class        MendPhotoSize
 *@description  修改头像组件
 *
 */
(function (factory, window) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define('MendPhotoSize', [], factory)
  } else if (typeof exports === 'object') {
    // Node/CommonJS style for Browserify
    module.exports = factory
  } else {
    // Browser globals
    window.MendPhotoSize = factory()
  }
})(function () {
  'use strict'
  const isServer = typeof window === 'undefined'
  // 扩展函数
  const extend = (target, params) => {
    Object.entries(params).forEach((item, index) => {
      'use strict'
      target[item[0]] = item[1]
    })
    return target
  }
  /**
   *
   * @name   hasClass
   * @time    2018-7-18
   * @param   el     {object}     目标元素
   * @param   cls     {className}     需要检测的class
   * @returns  ｛boolean｝      {boolean}
   * @description      判断目标元素是否包含某个class
   */
  const hasClass = (el, cls) => {
    if (!el || !cls) {
      return false
    }
    if (cls.indexOf(' ') !== -1) {
      throw new Error('className should not contain space.')
    }
    if (el.classList) {
      return el.classList.contains(cls)
    } else {
      return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1
    }
  }
  /**
   *
   * @name   addClass
   * @time   2018-07-18
   * @param   el     {object}     目标元素
   * @param   cls     {className}     需要添加的class
   * @description      给目标元素添加某个class
   */
  const addClass = (el, cls) => {
    if (!el) return
    var curClass = el.className
    var classes = (cls || '').split(' ')
    for (var i = 0, j = classes.length; i < j; i++) {
      var clsName = classes[i]
      if (!clsName) {
        continue
      }
      if (el.classList) {
        el.classList.add(clsName)
      } else if (hasClass(el, clsName)) {
        curClass += ' ' + clsName
      }
    }
    if (!el.classList) {
      el.className = curClass
    }
  }
  /**
   *
   * @name   removeClass
   * @time   2018-7-18
   * @param   el     {object}     目标元素
   * @param   cls     {className}     需要移除的class
   * @description      移除目标元素某个class
   */
  const removeClass = (el, cls) => {
    if (!el || !cls) return
    var classes = cls.split(' ')
    var curClass = ' ' + el.className + ' '
    for (var i = 0, j = classes.length; i < j; i++) {
      var clsName = classes[i]
      if (!clsName) {
        continue
      }
      if (el.classList) {
        el.classList.remove(clsName)
      } else if (hasClass(el, clsName)) {
        curClass = curClass.replace(' ' + clsName + ' ', ' ')
      }
    }
    if (!el.classList) {
      el.className = trim(curClass)
    }
  }

  /**
   *
   * @name   on
   * @time   2018-07-18
   * @param  element  {obejct}  需要绑定事件的对象或者元素
   * @param  event   ｛string｝ 需要绑定的事件名
   * @param  handler ｛function｝ 事件的回调函数
   * @description      为元素或对象绑定事件
   */
  const on = (() => {
    if (isServer) {
      return
    }
    if (document.addEventListener) {
      return function (element, event, handler) {
        if (element && event && handler) {
          element.addEventListener(event, handler, false)
        }
      }
    } else {
      return function (element, event, handler) {
        if (element && event && handler) {
          element.attachEvent('on' + event, handler)
        }
      }
    }
  })()
  /**
   *
   * @name   off
   * @time   2018-07-18
   * @param  element  {obejct}  需要解除事件的对象或者元素
   * @param  event   ｛string｝ 需要解除的事件名
   * @param  handler ｛function｝ 解除事件的回调函数
   * @description      为元素或对象解除事件
   */
  const off = (() => {
    if (isServer) {
      return
    }
    if (document.removeEventListener) {
      return function (element, event, handler) {
        if (element && event) {
          element.removeEventListener(event, handler, false)
        }
      }
    } else {
      return function (element, event, handler) {
        if (element && event) {
          element.detachEvent('on' + event, handler)
        }
      }
    }
  })()

  //  防抖函数;
  const debounce = (fn, _this, delay) => {
    let waiting
    const later = function (_arguments) {
      waiting = false
      fn.apply(_this, _arguments)
    }
    return function () {
      const _arguments = arguments
      // 每次进来清除定时器
      if (waiting) {
        clearTimeout(waiting)
      }
      // 设置定时器
      waiting = setTimeout(() => {
        // 执行回调函数
        later(_arguments)
      }, typeof delay === 'number' ? delay : 50)
    }
  }
  // 函数节流=> 某一段时间必须执行
  const throttle = (fn, _this, delay, mustRunDelay) => {
    let waiting
    let start = Date.now(); let now = 0
    const later = function (_arguments) {
      waiting = false
      fn.apply(_this, _arguments)
    }
    return function () {
      const _arguments = arguments
      now = Date.now()
      // 每次进来清除定时器
      if (waiting) {
        clearTimeout(waiting)
      }
      if (now - start >= mustRunDelay) {
        // console.log('超时了！')
        later(_arguments)
        start = Date.now()
      } else {
        // 设置定时器
        waiting = setTimeout(() => {
          /*
           *  console.log('走进来了！')
           * 执行回调函数
           */
          later(_arguments)
        }, typeof delay === 'number' ? delay : 50)
      }
    }
  }

  // 修改照片大小
  class MendPhotoSize {
    // 构造函数
    constructor (options) {
      /**
       * @class  MendPhotoSize
       * @desciption
       * @设计思路
       *     1.创建一个修改照片大小的类 MendPhotoSize()
       *     2.功能实现
       *        2.1.1  获photo容器元素，存在继续执行，不存在就不再执行，
       *        2.1.2 初始化参数
       *              命名空间 photoContain
       *              photoContain.width       // 容器宽
       *              photoContain.height      //  容器高
       *              photoContain.el          // 容器元素
       *
       *
       *
       *
       *        2.2.1 创建一个img元素  makeImg()， 命名空间img，设置id，追加到 容器中
       *        2.2.2  初始化参数
       *               img.el
       *               img.width
       *               img.height
       *               img.currentWidth
       *               img.currentHeight
       *               img.ratio
       *               img.ratioMin
       *               img.ratioMax
       *               img.top
       *               img.left
       *               img.currentTop
       *               img.currentLeft
       *
       *        2.2.3 加载图片      loadImg(utl[,base64])
       *        2.2.4 设置图片比率  setImgRatio(ratio) ;     this.img.ratio=1;   this.img.ratioMin=0.5;    this.img.ratioMax=5;
       *        2.2.5 设置图片大小  setImgSize()
       *                  this.img.width;          this.img.height;
       *                  this.img.currentWidth;   this.img.currentHeight;
       *        2.2.6 设置图片位置  countImgPosition(ratio)
       *                   this.img.top=ratio;   this.img.left=0.5;
       *        2.2.7  设置图片样式  setImgStyle()
       *        2.2.7  设置图片样式  updateImg()
       *
       *        2.3.1 创建一个canvas元素  makeCanvasMask()， 命名空间canvasMask，设置id，追加到容器中
       *        2.3.2  初始化参数
       *              canvasMask.el
       *              canvasMask.canvas
       *              canvasMask.width
       *              canvasMask.height
       *              canvasMask.cx         //  中心点x
       *              canvasMask.cy         //  中心点y
       *              canvasMask.cr         //  半径
       *              canvasMask.cacheId         //  缓存id
       *        2.3.3  绘制蒙版   drawCanvasMask()
       *
       *        2.4.1 创建一个canvas元素  makeCanvasImg()， 命名空间canvasImg，设置id，追加到容器中
       *        2.4.2  初始化参数
       *              canvasImg.el
       *              canvasImg.canvas
       *              canvasImg.width
       *              canvasImg.height
       *              canvasImg.cx         //  中心点x
       *              canvasImg.cy         //  中心点y
       *              canvasImg.cr         //  半径
       *              canvasImg.image         //  图片
       *        2.4.3  绘制照片   drawCanvasPhoto()
       *        2.4.4  生成照片   getCanvasPhoto(type)  @param  type {string}   图片类型 默认  image/png
       *        2.4.5  更新照片   drawCanvasUpdate()
       */
      // 安全模式  【important】
      if (!(this instanceof MendPhotoSize)) {
        throw new Error('必须使用 new 命令生成实例！')
      }
      // 绘制图片canvas
      const defaultOption = {
        // 页面上的元素
        id: '#jsCanvasStage', // 必须
        width: 0, // 容器宽  number类型，必须是正值
        height: 0, // 容器高  number类型，必须是正值
        /** 控制器相关参数**/
        controllerId: '#jsController',
        controllerBtnReduce: '.jsBtnReduce', // 缩小按钮
        controllerBtnAdd: '.jsBtnAdd', // 放大按钮
        controllerDragTrack: '.jsDragTrack', // 拖拽按钮
        controllerBtnDrag: '.jsBtnDrag', // 拖拽按钮
        /** 图片相关参数**/
        imgSrc: './images/photo.png',
        // 比率
        ratioRate: 0.2, // 增加的比率
        ratioMin: 0.2, // 最小的比率
        ratioMax: 5, // 最大比率
        // img 的class
        photoClass: 'fd-photo',
        // 隐藏图片的class
        photoHideClass: 'fd-hide',
        /** 图片蒙版相关参数**/
        imgMaskClass: 'fd-img-mask',
        /** 生成图片相关参数**/
        makeImgClass: 'fd-img-make',
        /* 其他参数 */
        throttleTime: 20, // 节流时间
        debounceTime: 10, // 防抖时间
        btnConfirm: '#jsConfirm', // 确定按钮
        btnCancel: '#jsCancel', // 取消按钮
        fileInput: '#jsFileLoad', // input：file
        // 确定按钮回调函数
        confirmCallback: function (data) {
          console.log(data)
          document.querySelector('#jsTestImg').src = data
        },
        // 取消按钮回调函数
        cancelCallback: function () {},
        // 上传按钮，文件改变回调函数
        fileChangeCallback: function () {}
      }
      /*
       * 合并参数 ,  Object.assin() 这个方法 babel转义很费劲，自己编写一个 extend 函数
       * 此处只用到了比较浅的赋值，所以没必要 extend方法写的比较简单
       */
      this.opts = extend(defaultOption, options || {})
      // 照片容器
      this.photoContainId = document.querySelector(this.opts.id)
      // 判断id的有效性
      if (this.photoContainId) {
        // 执行init方法
        this.init()
      } else {
        throw new Error('必须先制定有效的id!')
      }
    }

    // 初始化函数
    init () {
      // 初始化参数
      this.initParams()
    }

    // 初始化参数
    initParams () {
      'use strict'
      // 初始化照片容器参数
      this.initParamsPhotoContain()
      // 初始化控制器容器参数
      this.initParamsControllerContain()
      // 图片初始化
      this.img.init({
        parentElement: this.photoContainId,
        src: this.opts.imgSrc,
        ratioMin: this.opts.ratioMin, // 最小比率5倍
        ratioMax: this.opts.ratioMax, // 最大比率5倍
        ratioRate: this.opts.ratioRate, // 增加，减少的间隔数
        photoClass: this.opts.photoClass, // 增加，减少的间隔数
        photoHideClass: this.opts.photoHideClass // 增加，减少的间隔数
      })
      // 图片蒙版类初始化
      this.imgMask.init({
        parentElement: this.photoContainId,
        width: this.photoContain.width,
        height: this.photoContain.height,
        imgMaskClass: this.opts.imgMaskClass
      })
      // 生成图片类初始化
      this.makeImg.init({
        parentElement: this.photoContainId,
        width: this.photoContain.width,
        height: this.photoContain.height,
        makeImgClass: this.opts.makeImgClass
      }, this)

      // 绑定事件
      if (isServer) {
        this.bindEvent()
      }
    }

    // 初始化
    initParamsPhotoContain () {
      'use strict'
      // 容器矩形
      const _photoContainRect = this.photoContainId.getBoundingClientRect()
      // photoContain 命名空间
      this.photoContain = {
        el: this.photoContainId, // 容器元素
        width: this.opts.width ? this.opts.width : _photoContainRect.width, // 容器宽
        height: this.opts.height ? this.opts.height : _photoContainRect.height, //  容器高
        canDrag: false, // 是否可以拖动
        startX: 0, // 开始点 x
        startY: 0, // 开始点 y
        endX: 0, // 结束点  x
        endY: 0, // 结束点  y
        moveX: 0, // 移动点 x
        moveY: 0 // 移动点 y
      }
    }

    // 初始化控制器相关的参数
    initParamsControllerContain () {
      // 控制器容器id
      const _controllerElement = document.querySelector(this.opts.controllerId)
      // 判断id的有效性
      if (_controllerElement) {
        const _controllerDragTrack = _controllerElement.querySelector(this.opts.controllerDragTrack)
        const _controllerBtnDrag = _controllerElement.querySelector(this.opts.controllerBtnDrag)
        if (_controllerDragTrack && _controllerBtnDrag) {
          // controller 命名空间
          this.controller = {
            el: _controllerElement, // 控制器容器
            controllerDragTrack: _controllerDragTrack, // 滑道容器
            controllerDragTrackRect: _controllerDragTrack.getBoundingClientRect(), //  滑道的盒子大小
            topMax: null, // 最大top值
            topMin: null, //  最小top值
            topRange: null, //  top的范围值
            controllerBtnDrag: _controllerBtnDrag, //  被拖动的元素
            controllerBtnDragRect: _controllerBtnDrag.getBoundingClientRect(), // 被拖动的元素的大小
            currentTop: 0, // 当前的top值
            canDrag: 0, // 是否可以拖动
            startY: 0, // 开始点
            endY: 0, // 结束点
            moveY: 0 // 移动点
          }
          this.controller.topMax = this.controller.controllerDragTrackRect.height - this.controller.controllerBtnDragRect.height / 2
          this.controller.topMin = this.controller.controllerBtnDragRect.height / 2
          this.controller.topRange = this.controller.topMax - this.controller.topMin
          this.controller.currentTop = this.controller.topMin + this.controller.topRange / 2 // 默认top值
          // 初始化设置样式
          this.setControllerBtnDragStyle()
        } else {
          console.warn('controllerDragTrack 或者 controllerBtnDrag  未指定，不能使用控制器相关功能！')
        }
      } else {
        console.warn('controllerId 未指定，不能使用控制器相关功能！')
      }
    }

    // 获取照片
    getPhoto () {
      // 生成图片
      this.makeImg.makeImg(this.img.elCanvas, -this.img.currentLeft, -this.img.currentTop)
      //  返回照片
      return this.makeImg.getPhoto()
    }

    // 鼠标移动
    imgMousemove (event) {
      // 如果可以移动
      if (this.photoContain.canDrag) {
        // 结束点的位置
        this.endX = event.pageX
        this.endY = event.pageY
        // 移动距离
        this.moveX = this.endX - this.startX
        this.moveY = this.endY - this.startY
        // 移动图片的位置
        this.img.moveImg(this.moveX, this.moveY)
        // 赋值开始点
        this.startX = this.endX
        this.startY = this.endY
      }
    }

    // 鼠标按下
    imgMousedown (event) {
      this.photoContain.canDrag = true
      // 开始点的位置
      this.startX = event.pageX
      this.startY = event.pageY
    }

    // 鼠标滑出
    imgMouseout (event) {
      this.photoContain.canDrag = false
    }

    // 鼠标松开
    imgMouseup (event) {
      this.photoContain.canDrag = false
    }

    // 绑定图片的事件
    bindImgEvent () {
      /*
       * 鼠标移动
       *  性能优化=>函数节流 【important】
       */
      on(document, 'mousemove', throttle(this.imgMousemove, this, this.opts.debounceTime, this.opts.throttleTime))
      // 鼠标按下
      on(this.photoContain.el, 'mousedown', (event) => {
        this.imgMousedown(event)
      })
      // 鼠标滑出
      on(this.photoContain.el, 'mouseout', (event) => {
        this.imgMouseout(event)
      })
      // 鼠标松开
      on(document, 'mouseup', (event) => {
        this.imgMouseup(event)
      })
    }

    // 点击加号
    controllerAddClick () {
      const imgRatio = this.img.addImgRatio()
      // 设置current top值
      this.setControllerCurrentTop(imgRatio)
      // 设置拖拽按钮的样式
      this.setControllerBtnDragStyle()
    }

    // 点击减号
    controllerReduceClick () {
      const imgRatio = this.img.reduceImgRatio()
      // 设置current top值
      this.setControllerCurrentTop(imgRatio)
      // 设置拖拽按钮的样式
      this.setControllerBtnDragStyle()
    }

    // 设置current top值
    setControllerCurrentTop (ratio) {
      this.controller.currentTop = this.controller.topRange * ratio + this.controller.topMin
    }

    // 移动 current top值
    moveControllerCurrentTop (top) {
      this.controller.currentTop = this.controller.currentTop + top
      // 最后取值
      this.controller.currentTop = Math.max(Math.min(this.controller.currentTop, this.controller.topMax), this.controller.topMin)
    }

    // 设置拖拽按钮的样式
    setControllerBtnDragStyle () {
      this.controller.controllerBtnDrag.style = [
        'top:' + this.controller.currentTop + 'px'
      ].join('')
    }

    // 设置图片的ratio
    convertImgRatio () {
      let _ratio = 0
      switch (true) {
        case this.controller.currentTop == this.controller.topMin:
          _ratio = 0
          break
        case this.controller.currentTop == this.controller.topMax:
          _ratio = 1
          break
        default :
          _ratio = (this.controller.currentTop - this.controller.topMin) / this.controller.topRange
          break
      }
      // 调用图片的方法
      this.img.convertImgRatio(_ratio)
    }

    // 事件代理
    controllerClick (event) {
      switch (true) {
        case hasClass(event.target, this.opts.controllerBtnAdd.replace(/\./, '')):
          this.controllerAddClick()
          break
        case hasClass(event.target, this.opts.controllerBtnReduce.replace(/\./, '')):
          this.controllerReduceClick()
          break
        default :
          break
      }
    }

    // 鼠标按下
    controllerMousedown (event) {
      if (hasClass(event.target, this.opts.controllerBtnDrag.replace(/\./, ''))) {
        this.controller.canDrag = true
        // 开始点的位置
        this.controller.startY = event.pageY
      }
    }

    //  控制器鼠标按下
    controllerMouseup (event) {
      this.controller.canDrag = false
    }

    // 控制器鼠标移动
    controllerMousemove (event) {
      if (this.controller.canDrag) {
        // 结束点的位置
        this.controller.endY = event.pageY
        // 移动距离
        this.controller.moveY = this.controller.endY - this.controller.startY
        // 移动图片的位置
        this.moveControllerCurrentTop(this.controller.moveY)
        // 设置拖拽按钮的样式
        this.setControllerBtnDragStyle()
        // 改变图片的ratio
        this.convertImgRatio()
        // 赋值开始点
        this.controller.startY = this.controller.endY
      }
    }

    // 绑定控制器相关事件
    bindControllerEvent () {
      if (this.controller.el) {
        // 绑定点击事件
        on(this.controller.el, 'click', () => {
          this.controllerClick(event)
        })
        // 绑定鼠标按下事件
        on(this.controller.el, 'mousedown', () => {
          this.controllerMousedown(event)
        })
        // 绑定鼠标松开事件
        on(document, 'mouseup', () => {
          this.controllerMouseup(event)
        })
        /*
         * 绑定鼠标移动事件
         *  性能优化=>函数节流 【important】
         */
        on(document, 'mousemove', throttle(this.controllerMousemove, this, this.opts.debounceTime / 2, this.opts.throttleTime))
      }
    }

    // 绑定"确定按钮"事件
    bindConfirmEvent () {
      const _btnConfirm = document.querySelector(this.opts.btnConfirm)
      if (_btnConfirm) {
        on(_btnConfirm, 'click', () => {
          if (typeof this.opts.confirmCallback === 'function') {
            this.opts.confirmCallback(this.getPhoto())
          }
        })
      }
    }

    // 绑定"取消按钮"事件
    bindCancelEvent () {
      const _btnCancel = document.querySelector(this.opts.btnCancel)
      if (_btnCancel) {
        on(_btnCancel, 'click', () => {
          if (typeof this.opts.cancelCallback === 'function') {
            this.opts.cancelCallback()
          }
        })
      }
    }

    // 绑定"上传文件按钮"事件
    bindUploadFileEvent () {
      const _fileInput = document.querySelector(this.opts.fileInput)
      const _this = this
      if (_fileInput) {
        if (window.FileReader) {
          _this.fileRead = new FileReader()
          // 开始读取
          _this.fileRead.onloadstart = function (event) {
            // console.log('1.start1')
          }
          // 读取进度
          _this.fileRead.onprogress = function (event) {
            //  console.log('2.progress2')
          }
          // 开始加载
          _this.fileRead.onload = function (event) {
            //  console.log('3.load3')
          }
          _this.fileRead.onabort = function (event) {
            //  console.log('abort')
          }
          _this.fileRead.onerror = function (event) {
            // console.log('error')
          }
          // 加载完成可以使用
          _this.fileRead.onloadend = function (event) {
            /*
             * console.log('4.end')
             *  console.log(event.target.result)
             */
            _this.img.updateImg(event.target.result)
          }
        }
        // 绑定change事件
        on(_fileInput, 'change', (event) => {
          /*
           * console.log(_this.fileRead)
           * 取消读取
           */
          _this.fileRead.abort()
          // 读取文件
          _this.fileRead.readAsDataURL(_fileInput.files[0])
          // 回调函数
          if (typeof this.opts.fileChangeCallback === 'function') {
            this.opts.fileChangeCallback(_fileInput.files[0])
          }
        })
      }
    }

    // 绑定事件
    bindEvent () {
      // 绑定图片相关事件
      this.bindImgEvent()
      // 绑定控制器相关事件
      this.bindControllerEvent()
      // 绑定确定按钮事件
      this.bindConfirmEvent()
      // 绑定取消按钮事件
      this.bindCancelEvent()
      // 绑定上传文件按钮事件
      this.bindUploadFileEvent()
    }
  }

  // 生成图片的类
  MendPhotoSize.prototype.makeImg = {
    init: function (options) {
      // 初始化参数
      this.initParams(options)
      // 创建dom
      this.makeImgCanvasDom()
    },
    // 初始化参数
    initParams (options) {
      'use strict'
      // 父元素变量
      this.parentElement = options.parentElement
      // 获取宽高
      this.el = void (0)
      this.canvas = void (0)
      // 获取宽高
      this.width = options.width ? options.width : 226
      this.height = options.height ? options.height : 226
      this.cx = this.width / 2
      this.cy = this.height / 2
      this.cr = this.width / 2
      this.className = options.makeImgClass ? options.makeImgClass : 'fd-img-make'
    },
    // 生成图片dom
    makeImgCanvasDom () {
      'use strict'
      this.el = document.createElement('canvas')
      // alt 属性
      this.el.width = this.width
      // title属性
      this.el.height = this.height
      // 禁用拖拽（图片，文字浏览器默认是可以拖拽的）
      this.el.draggable = false
      // class
      this.el.className = this.className
      // 添加到dom
      if (this.parentElement) {
        this.parentElement.appendChild(this.el)
      }
      this.canvas = this.el.getContext('2d')
    },
    loadImage: function () {
      return new Promise((resolve, reject) => {
        var _img = new Image()
        _img.src = this.image
        _img.onload = (event) => {
          this.image = _img
          resolve('图片加载成功，可以使用了！')
        }
        _img.onerror = (event) => {
          reject('图片加载失败，图片不可使用！')
        }
      })
    },
    // 绘制照片
    drawImg (image, sx, sy) {
      'use strict'
      // 绘制图片
      this.canvas.drawImage(
        image,
        sx, sy, this.width, this.height,
        0, 0, this.width, this.height
      )
    },
    // 制作图片
    makeImg (image, sx, sy) {
      'use strict'
      // 清除画布
      this.canvas.clearRect(0, 0, this.width, this.height)
      this.canvas.lineWidth = 0
      this.canvas.beginPath()
      this.canvas.arc(this.cx, this.cy, this.cr, 0, 2 * Math.PI)
      // this.canvas.fill();
      this.canvas.closePath()
      // 裁剪图形
      this.canvas.clip()
      // 绘制照片
      this.drawImg(image, sx, sy)
    },
    // 获取图片
    getPhoto () {
      'use strict'
      return this.el.toDataURL('image/png')
    }
  }

  // 图片蒙版
  MendPhotoSize.prototype.imgMask = {
    init: function (options) {
      // 初始化参数
      this.initParams(options)
      // 创建dom
      this.makeImgMaskDom()
      // 绘制蒙版
      this.drawImgMask()
    },
    // 初始化参数
    initParams (options) {
      'use strict'
      // 父元素变量
      this.parentElement = options.parentElement
      // 获取宽高
      this.el = void (0)
      this.canvas = void (0)
      // 获取宽高
      this.width = options.width ? options.width : 226
      this.height = options.height ? options.height : 226
      this.cx = this.width / 2
      this.cy = this.height / 2
      this.cr = this.width / 2
      this.className = options.imgMaskClass ? options.imgMaskClass : 'fd-img-mask'
    },
    // 生成图片dom
    makeImgMaskDom () {
      'use strict'
      this.el = document.createElement('canvas')
      // alt 属性
      this.el.width = this.width
      // title属性
      this.el.height = this.height
      // 禁用拖拽（图片，文字浏览器默认是可以拖拽的）
      this.el.draggable = false
      // class
      this.el.className = this.className
      // 添加到dom
      if (this.parentElement) {
        this.parentElement.appendChild(this.el)
      }
      // 画布
      this.canvas = this.el.getContext('2d')
    },
    // 绘制蒙版
    drawImgMask () {
      'use strict'
      this.canvas.lineWidth = 0
      this.canvas.beginPath()
      this.canvas.arc(this.cx, this.cy, this.cr, 0, 2 * Math.PI)
      this.canvas.closePath()
      // 裁剪图形
      this.canvas.clip()
      // 绘制矩形
      this.canvas.fillRect(0, 0, this.width, this.height)
      // 反向
      this.canvas.putImageData(this.reverseOpacity(this.getImageData()), 0, 0)
    },
    // 获取图片的数据
    getImageData () {
      'use strict'
      return this.canvas.getImageData(0, 0, this.width, this.height)
    },
    // 反向透明度
    reverseOpacity (imageData, type) {
      //
      for (let y = 0; y < imageData.height; y++) {
        for (let x = 0; x < imageData.width; x++) {
          const index = y * imageData.width + x // index 为当前要处理的像素编号
          const p = index * 4 // 一个像素占四个字节，即 p 为当前指针的位置
          imageData.data[p + 3] = 255 - imageData.data[p + 3] // 将改变后的透明度存回数组
        }
      }
      return imageData
    }
  }

  // 图片类
  MendPhotoSize.prototype.img = {
    // 初始化的方法
    init (options) {
      'use strict'
      this.initParams(options)
      // 生成图片dom
      this.makeImgDom()
      // 默认加载一次图片
      this.loadImg(this.src)
    },
    // 初始化参数
    initParams (options) {
      'use strict'
      // 父元素变量
      this.parentElement = options.parentElement
      // 元素本身
      this.el = null
      // 元素本身对应canvas
      this.elCanvas = null
      // 图片路径
      this.src = options.src
      // 最小最大值
      this.topMin = 0
      this.topMax = 0
      this.leftMin = 0
      this.leftMax = 0
      this.top = 0
      this.left = 0
      //  top left的实际值
      this.currentTop = 0
      this.currentLeft = 0
      // 图片的原始宽和高
      this.width = 0
      this.height = 0
      // 图片展示时的宽高
      this.currentWidth = 0
      this.currentHeight = 0
      // 图片放大缩小的比率
      this.ratio = 1 // 默认比率

      this.ratioMin = options.ratioMin // 最小比率5倍
      this.ratioMax = options.ratioMax // 最大比率5倍
      this.ratioRate = options.ratioRate // 增加，减少的间隔数
      // 占比总次数
      this.ratioPercentCount = 0
      let value = 1
      while (value < this.ratioMax) {
        value = value * (1 + this.ratioRate)
        this.ratioPercentCount++
      }
      this.alt = '个人头像'
      this.title = '照片'
      // img 的class
      this.photoClass = options.photoClass
      // 隐藏图片的class
      this.photoHideClass = options.photoHideClass
    },
    // 生成图片dom
    makeImgDom () {
      'use strict'
      this.el = document.createElement('img')
      // alt 属性
      this.el.alt = this.alt
      // title属性
      this.el.title = this.title
      // 禁用拖拽（图片，文字浏览器默认是可以拖拽的）
      this.el.draggable = false
      // class
      this.el.className = this.photoClass
      // 默认隐藏
      this.hideImg()
      // 隐藏的canvas
      this.elCanvas = document.createElement('canvas')
      this.elCanvas.width = 0
      this.elCanvas.height = 0
      this.elCanvas.draggable = false
      this.elCanvas.style = [
        'position:absolute',
        'top:0',
        'left:0',
        'z-index:0',
        'visibility:hidden'
      ].join(';')
      // 添加到dom
      if (this.parentElement) {
        this.parentElement.appendChild(this.el)
        this.parentElement.appendChild(this.elCanvas)
      }
    },
    // 隐藏图片
    hideImg () {
      'use strict'
      // 添加class
      addClass(this.el, this.photoHideClass)
    },
    // 显示图片
    showImg () {
      'use strict'
      // 添加class
      removeClass(this.el, this.photoHideClass)
    },
    // 加载图片
    loadImg (url) {
      'use strict'
      if (!url) {
        return false
      }
      this.src = url
      new Promise((resolve, reject) => {
        const _img = new Image()
        _img.src = this.src
        _img.onload = () => {
          resolve(_img)
        }
        _img.onError = function () {
          reject('加载失败')
        }
      }).then(data => {
        // 获取图片的宽高
        this.width = data.width
        this.height = data.height
        // 重置图片的位置 （每次更换图片都需要把图片置于最初的位置）
        this.currentTop = 0
        this.currentLeft = 0
        // 1.设置图片的src
        this.setImgSrc()
        // 2.更新状态
        this.updateStatus()
        // 3..显示图片
        this.showImg()
      }, data => {
        console.error(data)
      })
    },
    // 设置图片的src
    setImgSrc () {
      'use strict'
      this.el.src = this.src
    },
    // 获取图片的src
    getImgSrc () {
      'use strict'
      return this.el.src
    },
    // 获取比率的百分比
    getRatioPercent () {
      'use strict'
      let _percent = 0
      let _currentValue = 1
      switch (true) {
        case this.ratio < 1:
          while (_currentValue > this.ratio) {
            _currentValue = _currentValue / (1 + this.ratioRate)
            _percent++
          }
          return ((this.ratioPercentCount - _percent) / this.ratioPercentCount * 0.5).toFixed(2)
          break
        case this.ratio == 1:
          return 0.5
          break
        case this.ratio > 1:
          while (_currentValue < this.ratio) {
            _currentValue = _currentValue * (1 + this.ratioRate)
            _percent++
          }
          return ((_percent / this.ratioPercentCount) * 0.5 + 0.5).toFixed(2)
          break
        default :
          break
      }
    },
    // 设置图片的比率，并且改变样式
    convertImgRatio (ratio) {
      'use strict'
      let _ratio = 0
      const _minRange = 1 - this.ratioMin
      const _maxRange = this.ratioMax
      switch (true) {
        case ratio < 0.5 :
          _ratio = _minRange * ratio + this.ratioMin
          break
        case ratio == 0.5:
          _ratio = 1
          break
        case ratio > 0.5:
          _ratio = _maxRange * (ratio - 0.5) * 2 + 1
          break
      }
      // 设置比率
      this.setImgRatio(_ratio)
      // 更新状态
      this.updateStatus(true)
    },
    // 缩小图片
    reduceImgRatio () {
      'use strict'
      this.ratio = this.ratio / (1 + this.ratioRate)
      // 设置比率
      this.setImgRatio(this.ratio)
      // 更新状态
      this.updateStatus(true)
      // 获取当前百分比
      return this.getRatioPercent()
    },
    // 放大图片
    addImgRatio () {
      'use strict'
      this.ratio = this.ratio * (1 + this.ratioRate)
      // 设置比率
      this.setImgRatio(this.ratio)
      // 更新状态
      this.updateStatus(true)
      // 获取当前百分比
      return this.getRatioPercent()
    },
    // 更新状态
    updateStatus (flag) {
      'use strict'
      // 1.设置图片的大小（这里需要按照ratio的值来取值）
      this.setImgSize()
      // 2.计算图片left and  top 的最大最小值
      this.calcMaxMinLeftAndTop()
      // 判断是否需要计算图片位置，只有重新上传图片才不需要计算位置
      if (flag) {
        // 计算图片的位置
        this.calcImgPosition()
      }
      // 3.设置样式
      this.setImgStyle()
    },
    // 设置图片的比率
    setImgRatio (ratio) {
      'use strict'
      // 计算比率
      this.ratio = Math.max(this.ratioMin, Math.min(this.ratioMax, ratio))
    },
    // 获取图片的比率
    getImgRatio () {
      'use strict'
      return this.ratio
    },
    // 同步绘制照片
    synchDrawCanvasPhoto () {
      'use strict'
      this.elCanvas.width = this.currentWidth
      this.elCanvas.height = this.currentHeight
      const _ctx = this.elCanvas.getContext('2d')
      // 清空画布
      _ctx.clearRect(0, 0, this.currentWidth, this.currentHeight)
      // 绘制图像
      _ctx.drawImage(this.el, 0, 0, this.width, this.height,
        0, 0, this.currentWidth, this.currentHeight
      )
    },
    // 设置图片大小
    setImgSize () {
      'use strict'
      this.currentWidth = this.ratio * this.width
      this.currentHeight = this.ratio * this.height
      // 同步绘制照片
      this.synchDrawCanvasPhoto()
    },
    // 计算最大最小top,left值
    calcMaxMinLeftAndTop () {
      'use strict'
      /*
       * 父容器的宽
       *  周密思考
       *  当图片小于父容器时， 图片应该可以在父容器下任意拖动。
       */
      const parentElementWidth = this.parentElement.getBoundingClientRect().width
      const parentElementHeight = this.parentElement.getBoundingClientRect().height
      //  top的最大值最小值
      this.topMin = parentElementHeight > this.currentHeight ? 0 : (parentElementHeight - this.currentHeight)
      this.topMax = parentElementHeight > this.currentHeight ? (parentElementHeight - this.currentHeight) : 0
      //  left 的最大值最小值
      this.leftMin = parentElementWidth > this.currentWidth ? 0 : (parentElementWidth - this.currentWidth)
      this.leftMax = parentElementWidth > this.currentWidth ? (parentElementWidth - this.currentWidth) : 0
    },
    // 计算图片位置
    calcImgPosition () {
      // 获取当前的图片的实际大小
      const _imgRect = this.el.getBoundingClientRect()
      // top，left 所在的百分比
      const _topPercent = this.top / _imgRect.height
      const _leftPercent = this.left / _imgRect.width
      // 实际的top，  left
      const _actTop = _topPercent * this.currentWidth
      const _actLeft = _leftPercent * this.currentHeight
      // 最终的top 和left值
      this.currentTop = Math.max(this.topMin, Math.min(_actTop, this.topMax))
      this.currentLeft = Math.max(this.leftMin, Math.min(_actLeft, this.leftMax))
    },
    // 设置图片样式
    setImgStyle () {
      this.top = this.currentTop
      this.left = this.currentLeft
      this.el.style = [
        'top:' + this.currentTop + 'px',
        'left:' + this.currentLeft + 'px',
        'width:' + this.currentWidth + 'px',
        'height:' + this.currentHeight + 'px'
      ].join(';')
    },
    // 移动图片
    moveImg (x, y) {
      'use strict'
      this.currentTop += y
      this.currentLeft += x
      // 最终的top 和left值
      this.currentTop = Math.max(this.topMin, Math.min(this.currentTop, this.topMax))
      this.currentLeft = Math.max(this.leftMin, Math.min(this.currentLeft, this.leftMax))
      // 设置图片样式
      this.setImgStyle()
    },
    // 更新图片
    updateImg (url) {
      // 加载图片
      this.loadImg(url)
    }
  }
  return MendPhotoSize
}, (isServer = typeof window === 'undefined' ? global : window))
